using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;

using UIKit;

using Outcoder.Collections;
using Outcoder.UI.Xaml;
using Outcoder.UI.Xaml.Resources;

using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(AppBar), typeof(Outcoder.UI.Xaml.Renderers.AppBarRenderer))]

namespace Outcoder.UI.Xaml.Renderers
{
	public class AppBarRenderer : ViewRenderer<AppBar, UIView>
	{
		string menuToolbarItemText = " ... ";

		bool initialized;
		static WeakReference<Page> toolbarOwner;
		IImageUrlTransformer imageUrlTransformer;

		protected override void OnElementChanged(ElementChangedEventArgs<AppBar> e)
		{
			base.OnElementChanged(e);

			var newAppBar = e.NewElement;

			if (!initialized)
			{
				initialized = true;

				imageUrlTransformer = Dependency.Resolve<IImageUrlTransformer, ImageUrlTransformer>(true);

				/* An Exception is raised if a control is not provided. */
				SetNativeControl(new UILabel(RectangleF.Empty));

				Page page = GetHostContentPage();
				page.Disappearing += (sender, args) =>
				{
					DetachAppBar(newAppBar);
					Page ownerPage;
					if (toolbarOwner != null && toolbarOwner.TryGetTarget(out ownerPage))
					{
						var hostContentPage = GetHostContentPage();
						if (ownerPage != hostContentPage)
						{
							return;
						}
					}

					RemoveToolbar();
				};

				page.Appearing += (sender, args) =>
				{
					Page ownerPage = GetHostContentPage();
					toolbarOwner = new WeakReference<Page>(ownerPage);
					UpdateAppBar();
					AttachAppBar(newAppBar);
				};
			}
		}

		void RemoveToolbar()
		{
			var page = GetHostNavigationPage() as Page;

			if (page == null)
			{
				page = GetHostContentPage();
			}

			if (page != null)
			{
				page.ToolbarItems.Clear();
			}
		}

		void AttachAppBar(AppBar appBar)
		{
			if (appBar == null)
			{
				return;
			}

			DetachAppBar(appBar);

			appBar.ButtonCollectionChanged += HandleButtonsChanged;
			appBar.MenuItemCollectionChanged += HandleMenuItemsChanged;
		}

		NavigationPage GetHostNavigationPage()
		{
			return GetParentOfType<NavigationPage>();
		}

		ContentPage GetHostContentPage()
		{
			return GetParentOfType<ContentPage>();
		}

		T GetParentOfType<T>() where T : class
		{
			T page = null;
			Element parent = Element;
			while (page == null)
			{
				parent = parent.Parent;
				if (parent == null)
				{
					break;
				}
				page = parent as T;
			}

			return page;
		}

		void DetachAppBar(AppBar appBar)
		{
			if (appBar == null)
			{
				return;
			}

			appBar.ButtonCollectionChanged -= HandleButtonsChanged;
			appBar.MenuItemCollectionChanged -= HandleMenuItemsChanged;
		}

		void HandleMenuItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			UpdateAppBar();
		}

		void HandleButtonsChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
		{
			UpdateAppBar();
		}

		void UpdateAppBar()
		{
			var appBar = Element;
			Page page = GetParentOfType<ContentPage>();

			if (page == null)
			{
				return;
			}

			List<ToolbarItem> items = null;

			foreach (IAppBarItem appBarItem in appBar.Buttons)
			{
				string text;

				if (!AppBarItemPropertyResolver.TryGetItemText(appBarItem, out text))
				{
					throw new Exception("Unable to resolve text for button.");
				}

				ToolbarItem item = new ToolbarItem { Name = text };

				IAppBarItem itemForClosure = appBarItem;
				item.Activated += (sender, e) => itemForClosure.PerformTap();

				string iconUrl;

				if (AppBarItemPropertyResolver.TryGetItemUrl(appBarItem, out iconUrl))
				{
					string transformedUrl = imageUrlTransformer.TransformForCurrentPlatform(iconUrl);
					item.Icon = transformedUrl;
				}

				if (items == null)
				{
					items = new List<ToolbarItem>();
				}

				items.Add(item);
			}

			var menuItemsEnumerable = appBar.MenuItems;
			if (menuItemsEnumerable != null)
			{
				var menuItems = menuItemsEnumerable.ToList();
				int menuItemCount = menuItems.Count;
				if (menuItemCount > 0)
				{
					string[] buttonTitles = new string[menuItemCount];
					for (int i = 0; i < menuItemCount; i++)
					{
						string text;

						if (!AppBarItemPropertyResolver.TryGetItemText(menuItems[i], out text))
						{
							throw new Exception("Unable to resolve text for menu item.");
						}

						buttonTitles[i] = text;
					}

					ToolbarItem item = new ToolbarItem { Name = menuToolbarItemText };
					item.Activated += async (sender, e) =>
					{
						var viewController = UIApplication.SharedApplication.Windows[0].RootViewController;
						var uiView = viewController.View;

						/* TODO: Make localizable resource. */
						var action = await DisplayMenu(uiView, /*AppResources.Global_Cancel*/"Cancel", buttonTitles);
						if (action > -1)
						{
							var selectedItem = menuItems[action];
							selectedItem.PerformTap();
						}
					};

					if (items == null)
					{
						items = new List<ToolbarItem>();
					}

					items.Add(item);
				}
			}

			if (items != null && items.Any())
			{
				var toolbarItems = page.ToolbarItems;
				toolbarItems.Clear();
				items.Reverse();
				toolbarItems.AddRange(items);
			}
		}

		Task<int> DisplayMenu(UIView uiView, string cancelText, params string[] buttons)
		{
			var sheet = new UIActionSheet();
			foreach (var button in buttons)
			{
				sheet.AddButton(button);
			}

			int cancelButtonIndex = Convert.ToInt32(sheet.ButtonCount);
			sheet.AddButton(cancelText);
			sheet.CancelButtonIndex = cancelButtonIndex;

			TaskCompletionSource<int> source = new TaskCompletionSource<int>();

			try
			{
				sheet.Clicked += delegate(object sender, UIButtonEventArgs args)
				{
					int result;

					if (args != null)
					{
						int buttonIndex = Convert.ToInt32(args.ButtonIndex);
						if (buttonIndex != cancelButtonIndex)
						{
							result = buttonIndex;
						}
						else
						{
							result = -1;
						}
					}
					else
					{
						result = -1;
					}

					source.SetResult(result);
				};

				sheet.ShowInView(uiView);
			}
			catch (Exception ex)
			{
				source.SetException(ex);
			}

			return source.Task;
		}
		
		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				var appBar = Element;
				if (appBar != null)
				{
					DetachAppBar(appBar);
				}
			}
			base.Dispose(disposing);
		}

		#region Trying to populate the toolbar like this appears to be contrary to the Xamarin page construction system
		//void UpdateItems(bool animated)
		//{
		//	var appBar = (AppBar)Model;
		//	var buttons = appBar.Buttons.ToList();
		//	UIToolbar baseControl = (UIToolbar)Control;
		//
		//	var items = (from c in buttons select ConvertToUIBarButtonItem(c)).ToArray<UIBarButtonItem>();
		//	baseControl.SetItems(items, animated);
		//}
		//
		//static UIBarButtonItem ConvertToUIBarButtonItem(IAppBarItem item)
		//{
		//	UIBarButtonItem uiButtonItem;
		//			
		//	if (item.IconUri != null)
		//	{
		//		string url = item.IconUri.ToString();
		//		UIImage image = new UIImage(url);
		//		uiButtonItem = new UIBarButtonItem(image.Scale(new SizeF(20f, 20f)), UIBarButtonItemStyle.Plain, null);
		//	}
		//	else
		//	{
		//		uiButtonItem = new UIBarButtonItem(item.Text, UIBarButtonItemStyle.Bordered, null);
		//	}
		//
		//	uiButtonItem.Clicked += delegate(object sender, EventArgs e)
		//		{
		//			item.PerformTap();
		//		};
		//	return uiButtonItem;
		//}

		#endregion
	}
}


